#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>


#include "global.h"
#include "constantes.h"
#include "sockets.h"
#include "configuracion.h"
#include "listas.h"
#include "NIPC.h"
#include "sincronizador.h"
#include "hiloDisco.h"


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void * HiloDeDisco (void * pNodoDisco)
{

	struct DiscoConocido *pDisco,*pDiscoALeer ;
	InfoIPC rInfoIPC;
	fd_set set_socketsHiloParaDisco;
	int iMayorSocket = 0 ;
	char *sNumero=(char *)malloc(20);
	char* aux;
	int idSinc;
	
	printf("Aca estoy \n");
	pDisco = (struct DiscoConocido *) pNodoDisco;
 	
	/*ACA RECIBE PEDIDOS DEL RAID Y LOS MANDA AL DISCO Y A LA VEZ RECIBE RESPUESTAS DE DISCOS Y SE LAS MANDA A LOS CLIENTES FSS*/	
	while(1)
	{	
		FD_ZERO(&set_socketsHiloParaDisco);
		FD_SET(*(pDisco->iSocketArrayParaHDA), &set_socketsHiloParaDisco);
		FD_SET(pDisco->iSocketHDAParaDA, &set_socketsHiloParaDisco);
		if(*(pDisco->iSocketArrayParaHDA) > iMayorSocket)
			iMayorSocket = *(pDisco->iSocketArrayParaHDA);	
		if(pDisco->iSocketHDAParaDA > iMayorSocket)
			iMayorSocket = pDisco->iSocketHDAParaDA;	
		select(iMayorSocket + 1,&set_socketsHiloParaDisco, NULL, NULL, NULL);
	  

		/****PEDIDO DEL RAID*****/
		if( FD_ISSET(*(pDisco->iSocketArrayParaHDA) , &set_socketsHiloParaDisco ) > 0 )	
		{
			
			InfoIPC rInfoIPC2;
			/*Recibo el numero del pedido y se lo envio al disco*/
			recibirIPC(*(pDisco->iSocketArrayParaHDA),&rInfoIPC2);
			enviarIPC(pDisco->iSocketHDAParaDA,&rInfoIPC2);
			
			InfoIPC rInfoIPC3;
			/*Recibo sector y se lo envio al disco*/
			recibirIPC(*(pDisco->iSocketArrayParaHDA),&rInfoIPC3);
			enviarIPC(pDisco->iSocketHDAParaDA,&rInfoIPC3);
			
			
			if(rInfoIPC3.payLoadDescriptor == WRITE_SECTOR_REQUEST)
			{
				InfoIPC rInfoIPC4;
				/*Recibo datos y se los envio al disco*/
				recibirIPC(*(pDisco->iSocketArrayParaHDA),&rInfoIPC4);
				enviarIPC(pDisco->iSocketHDAParaDA,&rInfoIPC4);
			}
		}
		
		
		/******RTA DEL DISCO*******/
		if( FD_ISSET(pDisco->iSocketHDAParaDA , &set_socketsHiloParaDisco ) > 0 )	
		{		
			
			
			/*Recibo el IDDescriptor del pedido*/
			InfoIPC rInfoIPC;
			
			if(recibirIPC(pDisco->iSocketHDAParaDA,&rInfoIPC)<0){
				printf("El disco %s se ha desconectado \n");
				reallocateoperations(pDisco);
				return NULL;
			}
			
			idSinc=atoi(rInfoIPC.payload);
			
			if (idSinc==-1){
			
				/*Recibo el numero de sector respondido*/
				InfoIPC rInfoIPC2;
				recibirIPC(pDisco->iSocketHDAParaDA,&rInfoIPC2);
				
				respuestSinc* respuesta=(respuestSinc*)malloc(sizeof(respuestSinc));
				respuesta->numeroSector=atoi(rInfoIPC2.payload);	
				respuesta->Sgt=NULL;
				respuesta->Ant=NULL;			
				
				struct ListaIDdescriptors *nodo= (struct ListaIDdescriptors *)BuscarPorNumeroSector(pDisco->listaPedidos,atoi(rInfoIPC2.payload));
				nodo->colaSincronizacion->numElementos ++;
			
		
				if(rInfoIPC2.payLoadDescriptor==RTA_READ_SECTORSYNC_REQUEST){
					/*Recibo la data*/
					InfoIPC rInfoIPC3;
					recibirIPC(pDisco->iSocketHDAParaDA,&rInfoIPC3);
					respuesta->operacion=0;
					memcpy(respuesta->data,rInfoIPC3.payload,512);		
				}
		
			
				if(rInfoIPC2.payLoadDescriptor==RTA_WRITE_SECTORSYNC_REQUEST){
					respuesta->operacion=1;
				}
		
				/*Agrego respuesta a cola de sincronizacion*/
				sem_wait(&(nodo->colaSincronizacion->full));
				sem_wait(&(nodo->colaSincronizacion->semaphore));
				
				AgregarRespuestaSinc(&respuesta,&(nodo->colaSincronizacion->listaRespuestasSinc),&(nodo->colaSincronizacion->listaRespuestasSinc_Ult));
				
				sem_post(&(nodo->colaSincronizacion->semaphore));
				sem_post(&(nodo->colaSincronizacion->empty));
			
			
				/*Saco pedido de la lista de pedidos del disco pues ya fue respondido*/
				if(pthread_mutex_lock(&mutex)!=0){
					perror("pthread_mutex_lock");
					exit(EXIT_FAILURE);
				}
					
				pedido *nodoaAtendido=(pedido *)SacarNodoDescriptoresDeLista2(&nodo,&pDisco->listaPedidos,&pDisco->listaPedidos_Ult);
			
				if(pthread_mutex_unlock(&mutex)!=0){
					perror("pthread_mutex_unlock");
					exit(EXIT_FAILURE);
				}
				free(nodo);
			}
			else
			{
				/****COMO ATIENDO PEDIDOS QUE NO SON DE SINCRONIZACION****/
			
								
				/*Recibo el numero de sector respondido*/
				InfoIPC rInfoIPC2;
				recibirIPC(pDisco->iSocketHDAParaDA,&rInfoIPC2);
	
				struct ListaIDdescriptors * nodo2=(struct ListaIDdescriptors *)BuscarDescriptorEnListaDeDisco(colaGlobal,rInfoIPC.payload);
				
				if(rInfoIPC2.payLoadDescriptor==RTA_READ_SECTOR_REQUEST){
				
					
					/*SACO DE COLA GLOBAL*/
					if(pthread_mutex_lock(&mutex)!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
					}
				
			
					SacarNodoDescriptoresDeLista2(&nodo2,&colaGlobal,&colaGlobal_Ult);
					imprimirCola2(colaGlobal);
				
					if(pthread_mutex_unlock(&mutex)!=0){
						perror("pthread_mutex_unlock");
						exit(EXIT_FAILURE);
					}
				
				
					/*SACO DE COLA DE DISCO*/
					if(pthread_mutex_lock(&(pDisco->mutex))!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
					}

					SacarNodoDescriptoresDeLista2(&nodo2,&(pDisco->listaPedidos),&(pDisco->listaPedidos_Ult));
					imprimirCola2(pDisco->listaPedidos);
					
					if(pthread_mutex_unlock(&(pDisco->mutex))!=0){
						perror("pthread_mutex_unlock");
						exit(EXIT_FAILURE);
					}
				
					
					/*Le envio al cliente FSS el numero de sector respondido*/
					enviarIPC(atoi(nodo2->sSocketPorElCualResponder),&rInfoIPC2);
					/*Recibo la data*/
					InfoIPC rInfoIPC3;
					recibirIPC(pDisco->iSocketHDAParaDA,&rInfoIPC3);
					/*Le envio al cliente FSS la data correspondiente*/
					enviarIPC(atoi(nodo2->sSocketPorElCualResponder),&rInfoIPC3);				
					
					free(nodo2);
				}
			
				if(rInfoIPC2.payLoadDescriptor==RTA_WRITE_SECTOR_REQUEST){
				
					
					if(nodo2->discosAEspejar>1)
					{
						printf("PASE POR DISCOS A ESPEJAR >1 \n");
						nodo2->discosAEspejar--;
						
					}
					else
					{	
						printf("PASE POR WRITE POR SEGUNDA VEZ \n");					
						/*SACO DE COLA GLOBAL*/
						if(pthread_mutex_lock(&mutex)!=0){
							perror("pthread_mutex_lock");
							exit(EXIT_FAILURE);
						}
					
						SacarNodoDescriptoresDeLista2(&nodo2,&colaGlobal,&colaGlobal_Ult);
						imprimirCola2(colaGlobal);
					
						if(pthread_mutex_unlock(&mutex)!=0){
							perror("pthread_mutex_unlock");
							exit(EXIT_FAILURE);																																													
						}
					
						printf("Antes de sacar de cola de disco \n");
					
						/*SACO DE COLA DE DISCO*/
						if(pthread_mutex_lock(&(pDisco->mutex))!=0){
							perror("pthread_mutex_lock");
							exit(EXIT_FAILURE);
						}

						SacarNodoDescriptoresDeLista2(&nodo2,&(pDisco->listaPedidos),&(pDisco->listaPedidos_Ult));
						imprimirCola2(pDisco->listaPedidos);
						
						if(pthread_mutex_unlock(&(pDisco->mutex))!=0){
							perror("pthread_mutex_unlock");
							exit(EXIT_FAILURE);
						}
					
						/*Le envio al cliente FSS el numero de sector respondido*/
						enviarIPC(atoi(nodo2->sSocketPorElCualResponder),&rInfoIPC2);	
						
						free(nodo2);
					}
				
					
				}
				
			}
			pDisco->pendings--;
			
		}	
			
	} 
}


void reallocateoperations(disco *dsk) {
	
	pedido *auxPedido;
	
	SacarDeLista_DA(&dsk);
	
	auxPedido=dsk->listaPedidos;
	while(auxPedido!=NULL){
		asignarPedido(&auxPedido);
		free(auxPedido);
	}
	
	free(dsk);
	
}



int VerificarExistenciaDiscoSincronizados()
{
    struct DiscoConocido * pAuxDisco;
    int icantidad = 0;
	InfoIPC *infoIPC;
	
    pAuxDisco = ListaDiscos;
    while(pAuxDisco != NULL)
    {
	if ((pAuxDisco->Estado == LISTO)  || (pAuxDisco->Estado == SINCRONIZANDO_LEYENDO))
	    icantidad ++;
	pAuxDisco = pAuxDisco->Sgt;
    }

    return icantidad;
}


void intacad ( int n , char* cadena)
{	
    int j=0;
    int i=0;
	char aux;

	do
    {
		cadena[i] = (n % 10) + '0';    
		n=n/10;
		i++;
	}while (n > 0);

    cadena[i] = '\0';
    i--;

	while(i>j)
	{
		aux=cadena[i];
		cadena[i]=cadena[j];
		cadena[j]=aux;
		i--;
		j++;
	}

}



